#include <fcntl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

#define SOCKET_ADDR "127.0.0.1"
#define BUFFER_SIZE 1024

typedef int (fill_data)(void* data);
typedef struct socket_buffer
{
    void* data;
    int len;
    struct socket_buffer* next;
}socket_buffer_t;

typedef struct
{
    /* ipv4 or ipv6 */
    int domain;
    /* tcp or udp */
    int type;
    /* client file description*/
    int c_fd;
    /* server file description*/
    int s_fd;
    /* socket address */
    struct sockaddr_in addr;
    /* store data needed to be transfered*/
    socket_buffer_t* buffer;

    int buffer_num;

    char* path;
} socket_handle_t;

socket_handle_t socket_handle;

int
max(int a,int b){
    return a>b?a:b;
}

int
min(int a,int b)
{
    return a<b?a:b;
}

int
create_socket()
{
    socket_handle_t* sh=&socket_handle;
    int err;
    
    /* init socket args*/
    sh->domain=AF_INET;
    sh->type=SOCK_STREAM;

    err=socket(sh->domain,sh->type,0);
    if(err<0){
        /* failed to create socket */
        return 1;
    }
    else{
        sh->c_fd=err;
        return 0;
    }
    // int flags=fcntl(sh->fd,F_GETFL,0);
    // /* set no_block mode for socket */
    // fcntl(sh->fd,F_SETFL,flags|O_NONBLOCK);
}

int
connect_server()
{
    socket_handle_t* sh=&socket_handle;
    /*init socket args*/
    sh->addr.sin_family=AF_INET;
    sh->addr.sin_port=8000;
    sh->addr.sin_addr.s_addr=inet_addr(SOCKET_ADDR);

    int err;
    err=connect(sh->c_fd,(struct sockaddr*)&sh->addr,sizeof(sh->addr));
    if(err!=0){
        printf("failed to connect server!\n");
        return 1;
    }
    return 0;
}

int
receive()
{
    socket_buffer_t buffer;
    socket_handle_t* sh=&socket_handle;

    buffer.data=(void*)malloc(BUFFER_SIZE);
    buffer.len=0;

    int count_out;
    do{
        count_out=0;
        count_out=read(sh->c_fd,buffer.data,BUFFER_SIZE);
        if(count_out<0){
            printf("failed to receive data!\n");
            return 1;
        }
        buffer.len=count_out;
        printf("this is received data: %s (%d)\n",(char*)buffer.data,count_out);
    }while(count_out>=BUFFER_SIZE);
    return 0;
}

int
transmit(fill_data fill,void* data)
{
    socket_handle_t* sh=&socket_handle;
    /*fill data*/
    fill(data);
    
    /*transfer data*/
    int buffer_num=sh->buffer_num;
    int next=0;
    for(int i=0;i<buffer_num;i++)
    {
        int count_out;
        do {
            count_out=0;
            count_out=write(sh->c_fd,sh->buffer[i].data+next,sh->buffer[i].len-next);
            if(count_out<0)
            {
                printf("failed to send data!\n");
                return 1;
            }
            else if(count_out<sh->buffer[i].len)
            {
                next=+count_out;
            }
            else
            {
                /* free buffer*/
                free(sh->buffer->data);
                next=0;
                sh->buffer[i].len=0;
            }
        }while(next<sh->buffer->len);
    }
    free(sh->buffer);
    return 0;
}

int
say_hello_to_server(void* data)
{
    socket_handle_t* sh=&socket_handle;

    sh->buffer=(socket_buffer_t*)malloc(sizeof(socket_buffer_t));
    char* str="hi, server!!!!\n";
    sh->buffer->len=strlen(str);
    sh->buffer->data=(void*)malloc(sizeof(str));
    strncpy((char*)sh->buffer->data,str,sh->buffer->len);
}

void
client_exit(int sig)
{
    socket_handle_t* sh=&socket_handle;
    
    close(sh->c_fd);
}

int
copy_data_from_command_once(void* data)
{
    socket_handle_t* sh=&socket_handle;
    char* transfer_data=(char*)data;
    printf("this is client!\n");
    /*allocate space for data*/
    sh->buffer->data=(void*)malloc(sizeof(data));
    // sh->buffer.data=data;
    sh->buffer->len=strlen(transfer_data);

    /*copy data to buffer*/
    strncpy((char*)sh->buffer->data,transfer_data,strlen(transfer_data));
    return 0;
}

int
copy_data_from_command_repeat(void* data)
{
    socket_handle_t* sh=&socket_handle;
    sh->buffer=(socket_buffer_t*)malloc(sizeof(socket_buffer_t)*sh->buffer_num);
    FILE* file=fopen(sh->path,"r");
    if(file==NULL)
    {
        printf("open file error\n");
    }
    int index=0;
    for(int i=0;i<sh->buffer_num;i++){
        int out=0;
        char* temp=(char*)malloc(BUFFER_SIZE);
        out=fscanf(file,"%s",temp);
        sh->buffer[i].data=(void*)temp;
        sh->buffer[i].len=BUFFER_SIZE;
    }
    return 0;
}

int
main(int argc,char** argv)
{
    signal(SIGINT,client_exit);

    int err;
    socket_handle_t* sh=&socket_handle;
    
    int len=atoi(argv[2]);
    sh->buffer_num=(len+BUFFER_SIZE-1)/BUFFER_SIZE;
    sh->path=(char*)malloc(sizeof(char)*strlen(argv[1]));
    strncpy((char*)sh->path,argv[1],strlen(argv[1]));

    err=create_socket();
    if(err!=0){
        printf("failed to create socket!\n");
        return 1;
    }

    err=connect_server();
    if(err!=0)
    {
        printf("failed to connect server!\n");
        return 1;
    }

    if(argc<2){
        printf("not found input data!\n");
        return 0;
    }
    else
    {
        clock_t start=clock();
        transmit(copy_data_from_command_repeat,(void*)argv[1]);;
        clock_t end=clock();
        float time=(start-end)/CLOCKS_PER_SEC;
        printf("%ld\t%ld\t%f\n",start,end,time);
    }
    close(sh->c_fd);
    return 0;
}